// rom書込をELFと比較でチェック。

#include "defines.h"
#include "lib.h"
#include "xmodem.h"
#include "kozos.h"

struct elf_header {
    struct {
        unsigned char magic[4];
        unsigned char class;
        unsigned char format;
        unsigned char version;
        unsigned char abi;
        unsigned char abi_version;
        unsigned char reserve[7];
    } id;
    short type;
    short arch;
    long version;
    long entry_point;
    long program_header_offset;
    long section_header_offset;
    long flags;
    short header_size;
    short program_header_size;
    short program_header_num;
    short section_header_size;
    short section_header_num;
    short section_name_index;    
};

struct elf_program_header {
    long type;
    long offset;
    long virtual_addr;
    long physical_addr;
    long file_size;
    long memory_size;
    long flags;
    long align;
};

static int elf_check(struct elf_header *header)
{
    if (memcmp(header->id.magic, "\x7f" "ELF", 4))
        return -1;
        
    if (header->id.class != 1) return -1;
    if (header->id.format != 2) return -1;
    if (header->id.version != 1) return -1;
    if (header->type != 2) return -1;
    if (header->version != 1) return -1;
    if ((header->arch != 46) && (header->arch != 47)) return -1;
    
    return 0;
}

int romtest(char *blockbuf, char *headerbuf)
{
    long offset = 0;
    int i, r, ret = 0;
    struct elf_header *ehdr = (struct elf_header *) headerbuf;
    unsigned char block_number = 1;
    struct elf_program_header *phdr;
    char *p;

    r = xmodem_recv_block(blockbuf, block_number++, 0);
    if (r <= 0)
        return -1;
    memcpy(headerbuf, blockbuf, r);
    offset += r;
    if (elf_check(ehdr) < 0)
        return -1;
    
    while (offset < ehdr->program_header_offset + ehdr->program_header_size * ehdr->program_header_num) {
        r = xmodem_recv_block(blockbuf, block_number++, 1);
        if (r <= 0)
            return -1;     
        memcpy(headerbuf + offset, blockbuf, r);
        offset += r;
    }

    for (i = 0; i < ehdr->program_header_num; i++) {
        phdr = (struct elf_program_header *) (headerbuf + ehdr->program_header_offset + ehdr->program_header_size * i);       
        if (phdr->type != 1)
            continue;
        p = (char *)phdr->physical_addr;

        if (phdr->file_size) {
            while (offset <= phdr->offset) {
                r = xmodem_recv_block(blockbuf, block_number++, 1);
                if (r <= 0)
                    return -1;
                offset += r;            
            }
            
            if (offset >= phdr->offset + phdr->file_size) {
                if (memcmp(p, blockbuf + r - (offset - phdr->offset), phdr->file_size)) {
                    ret = 1;
                    goto end;
                }
                p += phdr->file_size;
                
            } else {
                if (memcmp(p, blockbuf + r - (offset - phdr->offset), offset - phdr->offset)) {
                    ret = 1;
                    goto end;
                }
                p += offset - phdr->offset;
                
                while (1) {
                    r = xmodem_recv_block(blockbuf, block_number++, 1);
                    if (r <= 0)
                        return -1;
                    offset += r;            
                                    
                    if (offset >= phdr->offset + phdr->file_size)
                        break;
                    if (memcmp(p, blockbuf, r)) {
                        ret = 1;
                        goto end;
                    }
                    p += r;
                }
                if (memcmp(p, blockbuf, r - (offset - (phdr->offset + phdr->file_size)))) {
                    ret = 1;
                    goto end;
                }
                p += r - (offset - (phdr->offset + phdr->file_size));
            }
        }

//        memset(p, 0, phdr->memory_size - phdr->file_size);            
    }
    
end:
    while (xmodem_recv_block(blockbuf, block_number++, 1) > 0)  /* 読み終える */
        ;

    return ret;
}

int romtest_main(int argc, char *argv[])
{
    char *blockbuf, *headerbuf;
    
    blockbuf = kz_kmalloc(XMODEM_BLOCK_SIZE);
    headerbuf = kz_kmalloc(512);

    puts("Please send ROM.\n");

    int r;
    r = romtest(blockbuf, headerbuf);
    
    volatile long l;
    for (l = 0; l < 100000; l++)
        ;

    switch (r) {
        case 1:
            puts("Error found.\n");
            break;
        case 0:
            puts("No Errors.\n");
            break;
        case -1:
            puts("Input Error.\n");
            break;
        default:
            break;
    }

    return 0;
}
